package com.ruoyi.api.yh.pay;

/**
 * @ClassName WxPayV3ApiController
 * @Deacription TODO
 * @Author Administrator
 * @Date 2021/3/6 12:52
 * @Version 1.0
 **/

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.pagehelper.util.StringUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;

import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import com.ruoyi.api.yh.version.ApiVersion;
import com.ruoyi.common.config.WxConfig;
import com.ruoyi.common.constant.RedisConstant;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;

import com.ruoyi.common.utils.StringUtils;

import com.ruoyi.common.yh.api.R;
import com.ruoyi.common.yh.api.aop.LoginRequired;
import com.ruoyi.common.yh.api.dtovo.order.WxPayOrderVO;
import com.ruoyi.common.yh.api.util.ApiUserUtils;
import com.ruoyi.yh.domain.YhBingliInfo;
import com.ruoyi.yh.service.IYhBingliInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.dozer.Mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Validator;
import java.io.ByteArrayInputStream;
import java.io.FileWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.ijpay.core.enums.RequestMethod;
/**
 * @ClassName OrderApiController
 * @Deacription TODO
 * @Author Administrator
 * @Date 2021/3/5 13:38
 * @Version 1.0  改了版本号 要修改通知地址 这个比较特殊
 **/
@ApiSupport(order =4)
@Api(tags="微信下单API")
@RestController
@ApiVersion(1)
@RequestMapping("/{version}/app-api/wxPayV3")
public class WxPayV3ApiController {
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private Mapper dozerMapper;
    @Autowired
    private Validator validator;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private IYhBingliInfoService yhBingliInfoService;
   /* @Resource
    WxPayV3Bean wxPayV3Bean;*/
    @Autowired
    private WxConfig wxConfig;
    String serialNo="";
    String platSerialNo="";
    private boolean isactive=false;//false 代表测试  不用提提交微信支付支付接口


    @ApiOperationSupport(author = "chenmingming",order = 2)
    @ResponseBody
    @ApiOperation(value = "已创建的订单调用微信支付")
    @LoginRequired
    @GetMapping("/nativePayByorderId")
    public R<WxPayOrderVO> nativePayByorderId(@RequestParam String orderNo) {
       /* Set<ConstraintViolation<List<MatColorVO>>> failures = validator.validate(matColorVOS);
        //BeanValidators.extractPropertyAndMessage(failures).
        if (!failures.isEmpty()) {
            *//*  return R.fail(JSONArray.toJSONString(BeanValidators.extractPropertyAndMessage(failures)));*//*
            return R.fail(failures.iterator().next().getMessageTemplate());
        }*/
        SysUser businesTUser=   ApiUserUtils.getBusinesTUserr();
        if(StringUtil.isEmpty(orderNo)){
            return R.fail("生成订单异常1");
        }


        /**
         * @Author chenmingming
         * @Description //TODO 生成订单开始
         * @Date 10:18 2021/3/8
         * @Param [minOrderVO]
         * @return com.ruoyi.common.lvlv.api.R<java.lang.String>
         **/
        YhBingliInfo tOrder=yhBingliInfoService.selectYhBingliInfoByOrderNo(orderNo);
        if(tOrder==null){
            return R.fail("生成订单失败");
        }


        /**
         * @Author chenmingming
         * @Description //TODO 生成订单结束
         * @Date 10:18 2021/3/8
         * @Param [minOrderVO]
         * @return com.ruoyi.common.lvlv.api.R<java.lang.String>
         **/
        WxPayOrderVO wxPayOrderVO =new WxPayOrderVO();
        wxPayOrderVO.setOrderId(tOrder.getOrderNo());
       if(!isactive){

            return R.ok(wxPayOrderVO);
        }
        /**
         * @Author chenmingming
         * @Description //TODO 以下真实环境
         * @Date 14:20 2021/3/6
         * @Param [minOrderVO] rest.getData().getActualPrice()
         * @return com.ruoyi.common.lvlv.api.R<java.lang.String>
         **/
        try {

            int priAc= NumberUtil.mul(tOrder.getPayPrice(),100).intValue();//元转分求整数
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            WxConfig.Config config=    wxConfig.getConfig(WxConfig.lsAppId);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setAppid(config.getAppId())
                    .setMchid(config.getMchId())
                    .setDescription("绿驴批发")
                    .setOut_trade_no(tOrder.getOrderNo())
                    .setTime_expire(timeExpire)
                    .setAttach("绿驴批发")
                    .setNotify_url(config.getDomain().concat("/v1/wholesale-api/wxPayV3/payNotify"))
                    .setAmount(new Amount().setTotal(priAc)).setPayer(new Payer().setOpenid(businesTUser.getOpenid()));


            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    com.ijpay.core.enums.RequestMethod.POST,
                    WxDomain.CHINA.toString(),
                    WxApiType.JS_API_PAY.toString(),
                    config.getMchId(),
                    getSerialNumber(),
                    null,
                    config.getKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );
            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            if (response.getStatus() == 200) {
                // 根据证书序列号查询对应的证书来验证签名结果
                boolean verifySignature = WxPayKit.verifySignature(response, config.getPlatformCertPath());
                log.info("verifySignature: {}", verifySignature);
                if (verifySignature) {
                    String body = response.getBody();
                    JSONObject jsonObject = JSONUtil.parseObj(body);
                    String prepayId = jsonObject.getStr("prepay_id");
                    Map<String, String> map = WxPayKit.jsApiCreateSign(config.getAppId(), prepayId, config.getKeyPath());
                    log.info("唤起支付参数:{}", map);
                    log.info("verifySignature: {}", verifySignature);
                    wxPayOrderVO.setWxPayBody(response.getBody());
                    wxPayOrderVO.setMap(map);
                    wxPayOrderVO.setNonceStr(map.get("nonceStr"));
                    wxPayOrderVO.setPackageParm(map.get("package"));
                    wxPayOrderVO.setPaySign(map.get("paySign"));
                    wxPayOrderVO.setAppId(map.get("appId"));
                    wxPayOrderVO.setSignType(map.get("signType"));
                    wxPayOrderVO.setTimeStamp(map.get("timeStamp"));
                    wxPayOrderVO.setOrderId(tOrder.getOrderNo());
                }
            }
            //  boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());

            return R.ok(wxPayOrderVO,"下单成功");
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail(e.getMessage());
        }
    }
    @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
    @ResponseBody
    @ApiIgnore
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        if(!isactive){//模拟测试收到通知
String orderNo=request.getParameter("orderNo");
            YhBingliInfo tOrder=yhBingliInfoService.selectYhBingliInfoByOrderNo(orderNo);
            tOrder.setUpdateTime(new Date());
            tOrder.setStatus("2");
            tOrder.setPayType("1");
            //tOrder.setFserialcode(String.valueOf(new Date().getTime()));
            yhBingliInfoService.updateYhBingliInfo(tOrder);
return;
        }
        Map<String, String> map = new HashMap<>(12);
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);
            WxConfig.Config config=    wxConfig.getConfig(WxConfig.lsAppId);
            // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    config.getApiKey3(), config.getPlatformCertPath());

            log.info("支付通知明文 {}", plainText);

            if (StrUtil.isNotEmpty(plainText)) {
                /**
                 * @Author chenmingming
                 * @Description //TODO 更新订单
                 * @Date 10:33 2021/3/26
                 * @Param [request, response]
                 * @return void
                 **/
            JSONObject json=    JSONUtil.parseObj(plainText);
            boolean f=true;
                if("SUCCESS".equals(json.getStr("trade_state"))){
                    String orderCode=json.getStr("out_trade_no");
                    JSONObject amount=json.getJSONObject("amount");
                    int payer_total=amount.getInt("payer_total");
                    YhBingliInfo tOrder=  yhBingliInfoService.selectYhBingliInfoByOrderNo(orderCode);
                    if(null!=tOrder){
                        int priAc= NumberUtil.mul(tOrder.getPayPrice(),100).intValue();//元转分求整数
                        if(priAc==payer_total){
                           // tOrder.setFpaytime(new Date());
                            tOrder.setStatus("2");
                            //tOrder.setFserialcode(json.getStr("transaction_id"));
                            yhBingliInfoService.updateYhBingliInfo(tOrder);

                            response.setStatus(200);
                            map.put("code", "SUCCESS");
                            map.put("message", "SUCCESS");
                           // redisCache.deleteObject(RedisConstant.API_ORDER_CODE+"_"+tOrder.getFordercode());//移除redis的存储
                            f=false;

                        }else{

                        }

                    }

                }

                /**
                 * @Author chenmingming
                 * @Description //TODO 更新订单结束
                 * @Date 10:33 2021/3/26
                 * @Param [request, response]
                 * @return void
                 **/
                if(f){
                    response.setStatus(500);
                    map.put("code", "ERROR");
                    map.put("message", "金额不对");
                }

            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private String getSerialNumber() {
                  if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
                      WxConfig.Config config=    wxConfig.getConfig(WxConfig.lsAppId);
            X509Certificate certificate =     PayKit.getCertificate(FileUtil.getInputStream(config.getCertPath()));
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }
    @RequestMapping("/get")
    @ResponseBody
    @ApiIgnore
    public String v3Get() {
        // 获取平台证书列表
        try {
            WxConfig.Config config=    wxConfig.getConfig(WxConfig.lsAppId);
            IJPayHttpResponse response = WxPayApi.v3(
                    com.ijpay.core.enums.RequestMethod.GET,
                    WxDomain.CHINA.toString(),
                    WxApiType.GET_CERTIFICATES.toString(),
                    config.getMchId(),
                    getSerialNumber(),
                    null,
                    config.getKeyPath(),
                    ""
            );

            String timestamp = response.getHeader("Wechatpay-Timestamp");
            String nonceStr = response.getHeader("Wechatpay-Nonce");
            String serialNumber = response.getHeader("Wechatpay-Serial");
            String signature = response.getHeader("Wechatpay-Signature");

            String body = response.getBody();
            int status = response.getStatus();

            log.info("serialNumber: {}", serialNumber);
            log.info("status: {}", status);
            log.info("body: {}", body);
            int isOk = 200;
            if (status == isOk) {
                JSONObject jsonObject = JSONUtil.parseObj(body);
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String serialNo = encryptObject.getStr("serial_no");
                final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, config.getPlatformCertPath());
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, config.getPlatformCertPath());
            System.out.println("verifySignature:" + verifySignature);
            return body;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
        try {
            WxConfig.Config config=    wxConfig.getConfig(WxConfig.lsAppId);
            AesUtil aesUtil = new AesUtil(config.getApiKey3().getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
            FileWriter writer = new FileWriter(certPath,true);
            writer.write(publicKey);
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }
}
